#include "gateway.h"
#include "gateServer.h"

bool g_bIsAlive = false;

bool IsRunning()
{
	return g_bIsAlive;
}

void CloseServer()
{
	g_bIsAlive = false;
}

Epoll::~Epoll()
{
#ifdef WIN32
#elif defined(LINUX)
	for(uint16 i = 0; i < m_events; ++i)
		SAFE_DELETE(m_events[i]);
	SAFE_DELETE_ARRAY(m_events);

	close(m_epfd);
#endif
}

void Epoll::PushGateway(Connection* gateway)
{
	m_mutex.Lock();
	m_gateways.push_back(gateway);
	m_mutex.Unlock();
}

void Epoll::Init(uint16 eventSize, int listenfd)
{
	m_eventSize = eventSize;
	m_listenfd = listenfd;

#ifdef WIN32
#elif defined(LINUX)
	m_events = SAFE_NEW epoll_event*[m_eventSize];
	for(uint16 i = 0; i < m_events; ++i)
		m_events[i] = SAFE_NEW epoll_event();

	m_epfd = epoll_create(m_eventSize);
#endif
}

int Epoll::_Kernel()
{
	if(!m_gateways.empty())
	{
		m_mutex.Lock();

		for(list<Connection*>::const_iterator it = m_gateways.begin(); it != m_gateways.end(); it++)
		{
			INSTALL_EPOLL_MAP((*it)->GetSocket()->GetSocket(), (*it));

#ifdef WIN32
#elif defined(LINUX)
			m_ev.data.fd = (*it)->GetSocket();
			m_ev.events = EPOLLIN | EPOLLET;
			epoll_ctl(m_epfd, EPOLL_CTL_ADD, (*it)->GetSocket(), &m_ev);
#endif
		}

		m_gateways.clear();

		m_mutex.Unlock();
	}
	
	if(!m_sockets.empty())
		Handle();

	return 0;
}

#ifdef WIN32

void Epoll::Handle()
{
	struct timeval tval = { 0, 0 };

	FD_ZERO(&m_writeSet);
	FD_ZERO(&m_readSet);
	
	int	maxSock = -1;
	for(SocketMap::iterator it = m_sockets.begin(); it != m_sockets.end(); ++it)
	{		
		int sock = it->first;
		maxSock = maxSock < sock ? sock : maxSock;

		FD_SET(sock, &m_writeSet);
		FD_SET(sock, &m_readSet);
	}

	int value = select(maxSock + 1, &m_readSet, &m_writeSet, NULL, &tval);
	if(value <= 0)
		return;

	for(SocketMap::iterator it = m_sockets.begin(); it != m_sockets.end();)
	{		
		int sock = it->first;
		Connection* con = (*it).second;

		if(con != NULL)
		{
			if(FD_ISSET(sock, &m_writeSet))
				con->HandleSendSocket();
			if(FD_ISSET(sock, &m_readSet))
			{
				if(sock == m_listenfd)
					con->Accept();
				else
					con->HandleRecvSocket();
			}

			if(!con->IsAlive())
			{
				con->SetInSocketSignal();
				m_sockets.erase(it++);
				continue;
			}
		}
		else
		{
			ASSERT(0);
		}

		++it;
	}
}

#elif defined(LINUX)

void Epoll::Handle()
{
	int nfds = epoll_wait(m_epfd, m_events, m_eventSize, 0);

	for(int i=0; i < nfds; ++i)
	{
		Connection* con = NULL;

		SocketMap::iterator it = m_sockets.find(m_events[it].data.fd);
		if(it != m_sockets.end() && (*it).second != NULL)
			con = (*it).second;
		else
		{
			ASSERT(0);
			break;
		}

		if(m_events[i].data.fd == m_listenfd)
		{
			con->Accept();
		} 
		else if(m_events[i].events & EPOLLIN)
		{ 
			if (events[i].data.fd < 0)
			{
				ASSERT(0);
				continue; 
			}

			con->HandleRecvSocket();
			
			m_ev.data.fd = m_events[i].data.fd;
			m_ev.events = EPOLLOUT | EPOLLET;
		} 
		else if(events[i].events & EPOLLOUT)
		{
			if (events[i].data.fd < 0)
			{
				ASSERT(0);
				continue; 
			}

			con->HandleSendSocket();
		}

		if(con->IsAlive())
		{
			epoll_ctl(epfd, EPOLL_CTL_MOD, m_events[i].data.fd, &m_ev);
		}
		else
		{
			epoll_ctl(m_epfd, EPOLL_CTL_DEL, m_events[i].data.fd, &m_ev);
			con->SetInSocketSignal();
			m_sockets.erase(it);
		}
	} 
}

#endif

GateServer::GateServer()
: Connection(SOCKET_TYPE_TCP)
{
}

GateServer::~GateServer()
{
	m_epoll.Stop();
}

void GateServer::Init(vector<string>& configs)
{
	if(!configs.empty())
	{
	}

	g_bIsAlive = m_socket->Bind(8000);
	if(g_bIsAlive)	g_bIsAlive = m_socket->Listen();
	if(g_bIsAlive)  g_bIsAlive = m_socket->SetNonBlocking();
	if(g_bIsAlive)	
	{
		m_epoll.Init(100, m_socket->GetSocket());
		m_epoll.PushGateway(this);
		m_epoll.Start();
	}
}

void GateServer::PushGateway(Gateway* gateway)
{
	m_mutex.Lock();
	m_pendingGateways.push_back(gateway);
	m_mutex.Unlock();
}

bool GateServer::Accept()
{
	//if(m_socket->Select(SOCKET_SELECT_READ))
	{
		int		socket;
		char*	ip = NULL;
		uint16	port;

		if(m_socket->Accept(socket, ip, port))
		{
			Gateway* gateway = SAFE_NEW Gateway(socket, ip, port);			
			gateway->GetSocket()->SetNonBlocking();

			PushGateway(gateway);
			m_epoll.PushGateway(gateway);
		}
		else
			return false;

		SAFE_DELETE_ARRAY(ip);
	}

	return true;
}

int GateServer::Update()
{
	if(!m_pendingGateways.empty())
	{
		m_mutex.Lock();

		for(list<Gateway*>::const_iterator it = m_pendingGateways.begin(); it != m_pendingGateways.end(); it++)
			m_gateways.push_back(*it);
		m_pendingGateways.clear();

		m_mutex.Unlock();
	}

	for(list<Gateway*>::const_iterator it = m_gateways.begin(); it != m_gateways.end();)
	{
		Gateway* gateway = *it;
		if(gateway->GetInServerSignal())
			(*it)->Update();
		else if(!gateway->GetInSocketSignal())
		{
			it = m_gateways.erase(it);
			SAFE_DELETE(gateway);
			continue;
		}

		++it;
	}

	return 0;
}

LoginResult	GateServer::Login(const string& uid, Gateway* gateway)
{
	m_mutex.Lock();

	LoginResult result;

	LoginMap::iterator i = m_loginMap.find(uid);

	if(i != m_loginMap.end() && (*i).second != NULL)
	{
		result = LOGINRESULT_USER_EXIST;
	}
	else
	{
		m_loginMap[uid] = gateway;
		
		result = LOGINRESULT_SUCCESS;
	}

	m_mutex.Unlock();

	return result;
}

LogoutResult GateServer::Logout(const string& uid, Gateway* gateway)
{
	m_mutex.Lock();

	LogoutResult result;

	LoginMap::iterator i = m_loginMap.find(uid);

	if(i != m_loginMap.end())
	{
		if((*i).second != gateway)
		{
			ELOG("%s: user exit failed, LOGOUTRESULT_USER_NOT_MATCH\n", uid.c_str());
		}
		
		m_loginMap.erase(i);

		result = LOGOUTRESULT_SUCCESS;
	}
	else
	{
		result = LOGOUTRESULT_USER_NOT_EXIST;
	}
	
	m_mutex.Unlock();

	return result;
}

void GateServer::Kickout(const string& uid)
{
	m_mutex.Lock();

	LoginMap::iterator i = m_loginMap.find(uid);

	if(i != m_loginMap.end() && (*i).second != NULL)
	{
		Gateway* gateway = (*i).second;
		gateway->Disconnect();
	}
	
	m_mutex.Unlock();
}